﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CacheProxy
{
    public static class FallbackTaskQueue
    {
        private static ConcurrentQueue<Task> _taskQueue;
        private static Task _initialTask;
        private static Task _queueTail;
        private static int _counter = 0;
        private static object _syncblock = new object();
        private static LinkedList<Task> _taskList;
        static FallbackTaskQueue()
        {
            _taskQueue = new ConcurrentQueue<Task>();
            _taskList = new LinkedList<Task>();
        }
        public static T EnlistTask<T>(Func<string, T> fallback, string key)
        {
            //Task queueHead;
            Task<T> fallbackTask = new Task<T>(() =>
            {
                //_taskQueue.TryDequeue(out queueHead);
                Interlocked.Add(ref _counter, 1);
                Debug.WriteLine("head tid:" + Thread.CurrentThread.ManagedThreadId + "; counter:" + _counter);
                Interlocked.Add(ref _counter, -1);
                return fallback(key);
            });
            //_taskQueue.Enqueue(fallbackTask);
            //Debug.WriteLine("queue length:"+_taskQueue.Count);
            //Thread.Sleep(5000);

            //if (_taskQueue.TryDequeue(out queueHead))
            //{
            if (_initialTask == null)
            {
                lock (_syncblock)
                {
                    if (_initialTask == null)
                    {
                        _initialTask = fallbackTask;
                        _queueTail = fallbackTask;
                        fallbackTask.Start();
                        T result = fallbackTask.Result;
                        _initialTask = null;
                        return result;
                    }
                    else
                    {
                        var task = _queueTail.ContinueWith<T>(t =>
                        {
                            Interlocked.Add(ref _counter, 1);
                            Debug.WriteLine("tid:" + Thread.CurrentThread.ManagedThreadId + ";counter:" + _counter);
                            Interlocked.Add(ref _counter, -1);
                            return fallback(key);
                        }, TaskContinuationOptions.LongRunning);
                        _queueTail = task;
                        return task.Result;
                    }
                }
            }

            else
            {
                var task = _queueTail.ContinueWith<T>(t =>
                {
                    _queueTail = t;
                    Interlocked.Add(ref _counter, 1);
                    Debug.WriteLine("tid:" + Thread.CurrentThread.ManagedThreadId + ";counter:" + _counter);
                    Interlocked.Add(ref _counter, -1);
                    return fallback(key);
                }, TaskContinuationOptions.LongRunning);
                _queueTail = task;
                return task.Result;
            }
        }
        static int _queueLength;
        public static T EnqueueTask<T>(Func<string, T> fallback, string key)
        {
            if (_initialTask == null)
            {
                lock (_syncblock)
                {
                    if (_initialTask == null)
                    {
                        _initialTask = new Task(() =>
                        {
                            ////_taskQueue.TryDequeue(out queueHead);
                            //Interlocked.Add(ref _counter, 1);
                            Debug.WriteLine("head tid:" + Thread.CurrentThread.ManagedThreadId);
                            //Interlocked.Add(ref _counter, -1);
                            //return fallback(key);
                            return;
                        });
                        _queueTail = _initialTask;
                        _taskQueue.Enqueue(_initialTask);
                    }
                }
            }
            Task<T> fallbackTask = new Task<T>(() =>
            {
                //_taskQueue.TryDequeue(out queueHead);
                Interlocked.Add(ref _counter, 1);
                Debug.WriteLine("head tid:" + Thread.CurrentThread.ManagedThreadId + "; counter:" + _counter);
                Interlocked.Add(ref _counter, -1);
                return fallback(key);
            });
            _taskQueue.Enqueue(fallbackTask);

            var task = _queueTail.ContinueWith<T>(t =>
            {
                Interlocked.Add(ref _counter, 1);
                Debug.WriteLine("tid:" + Thread.CurrentThread.ManagedThreadId + ";counter:" + _counter);
                Interlocked.Add(ref _counter, -1);
                return fallback(key);
            }, TaskContinuationOptions.LongRunning);
            _queueTail = task;
            Interlocked.Add(ref _queueLength, 1);
            if (_queueLength > 0 && _queueLength % 5 == 0)
            {
                _queueLength = 0;

                _initialTask.Start();
                _initialTask = null;
            }
            return task.Result;
        }
    }
}
